#pragma once

#include <vector>
#include <queue>
#include "Thread.hpp"
#include "Mutex.hpp"
#include <unistd.h>
#include <pthread.h>

namespace threadpool
{
    using namespace std;
    using namespace thread;
    using namespace mutex;

    template <class T>
    class threadPool;

    template <class T>
    class threadData /*传递给线程例程的参数*/
    {
    public:
        threadPool<T> *_this;
        string _name;
    };

    template <class T>
    class threadPool
    {
    private:
#define INIT_THREAD_NUM 10
        static void *start_routine(void *args);

    private:
        void queueLock() { pthread_mutex_lock(&_mutex); }
        void queueUnlock() { pthread_mutex_unlock(&_mutex); }
        bool isQueueEmpty() { return _taskQueue.empty(); }
        void queueWait() { pthread_cond_wait(&_cond, &_mutex); }
        T taskPop();
        pthread_mutex_t *mutex() { return &_mutex; }

    private:
        /*将构造函数作为私有，并且禁用拷贝构造和赋值重载
         *目的就是为了不允许在类外定义对象*/
        threadPool(int num = INIT_THREAD_NUM);
        threadPool(const threadPool &tp) = delete;
        threadPool &operator=(const threadPool &tp) = delete;

    public:
        ~threadPool();
        void start();
        void push(const T &in);

        /*给定一个静态方法，用来获取单例对象的指针
         *静态方法可以不用对象访问*/
        static threadPool<T> *getInstance()
        {
            /*如果没有最外层的这条判断语句，那么每个线程要创建对象时都要加锁
             *如果在加锁之前告诉线程对象已经存在了，线程就不用再加锁了
             *从而减少加锁和解锁所带来的开销*/
            if (_singleton == nullptr)
            {
                /*如果单例指针为空，说明对象还没有被实例化过*/
                pthread_mutex_lock(&_singletonMutex);
                if (_singleton == nullptr)
                {
                    _singleton = new threadPool<T>();
                }
                pthread_mutex_unlock(&_singletonMutex);
            }
            return _singleton;
        }

    private:
        vector<Thread *> _threadVec;
        queue<T> _taskQueue;
        pthread_mutex_t _mutex;
        pthread_cond_t _cond;

        /*单例指针和多线程创建对象时的锁*/
        static threadPool<T> *_singleton;
        static pthread_mutex_t _singletonMutex;
    };
    template <class T>
    threadPool<T> *threadPool<T>::_singleton = nullptr;
    template <class T>
    pthread_mutex_t threadPool<T>::_singletonMutex = PTHREAD_MUTEX_INITIALIZER;
    
    template <class T>
    void *threadPool<T>::start_routine(void *args)
    {
        threadData<T> *td = static_cast<threadData<T> *>(args);
        while (true)
        {
            T task;
            {
                LockGuard lockguard(td->_this->mutex());
                while (td->_this->isQueueEmpty())
                { /*如果队列为空就说明没有任务，没有任务就去条件变量阻塞*/
                    td->_this->queueWait();
                }
                task = td->_this->taskPop();
                td->_this->queueUnlock();
            }
            /*获取任务是互斥的，但是执行任务是并发的*/
            // cout << td->_name << " get a task: " << task.getTaskName() << " result: " << task() << endl;
            task();
        }
    }

    template <class T>
    T threadPool<T>::taskPop()
    {
        T task = _taskQueue.front();
        _taskQueue.pop();
        return task;
    }

    template <class T>
    threadPool<T>::threadPool(int num)
        : _threadVec(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for (int i = 0; i < _threadVec.size(); i++)
        {
            /*构造函数只负责创建线程对象
             *参数的传递在start方法中*/
            _threadVec[i] = new Thread();
        }
    }

    template <class T>
    threadPool<T>::~threadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

    /*提供给外部的接口，外部调用start接口
     *就说明线程池启动，可以投递任务*/
    template <class T>
    void threadPool<T>::start()
    {
        for (auto &e : _threadVec)
        {
            threadData<T> *td = new threadData<T>();
            td->_name = e->thread_name();
            td->_this = this;

            e->start(start_routine, td);
            cout << e->thread_name() << " start ..." << endl;
        }
    }

    /*向线程池当中添加任务*/
    template <class T>
    void threadPool<T>::push(const T &in)
    {
        LockGuard lockguard(&_mutex);
        _taskQueue.push(in);
        pthread_cond_signal(&_cond);
    }

} /*namespace threadpool ends here*/
